package cn.sunzq.springdata.elasticsearch.demo.utils;

import cn.sunzq.springdata.elasticsearch.demo.base.BaseService;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.TermsQueryBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.IndexOperations;
import org.springframework.data.elasticsearch.core.document.Document;
import org.springframework.data.elasticsearch.core.index.AliasAction;
import org.springframework.data.elasticsearch.core.index.AliasActionParameters;
import org.springframework.data.elasticsearch.core.index.AliasActions;
import org.springframework.data.elasticsearch.core.index.AliasData;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.IndexQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Component;

import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * @创建人 孙中泉
 * @创建时间 2019/9/21
 * @描述
 */
@Component
public class ElasticsearchOperationstUtil extends BaseService {

    private final Logger LOGGER = LoggerFactory.getLogger(ElasticsearchOperationstUtil.class);

    /**
     * @描述
     * @参数[o]
     * @返回值 java.lang.String
     * @创建人 孙中泉
     * @创建时间 2019/10/18
     * @修改人和其他信息
     */
    public String toJSONString(Object o) {
        return JSONObject.toJSONString(o, SerializerFeature.WriteDateUseDateFormat);
    }

    public boolean existsIndex(String indexName){
        IndexCoordinates indexCoordinates = IndexCoordinates.of(indexName);
        IndexOperations indexOperations = elasticsearchOperations.indexOps(indexCoordinates);
        return indexOperations.exists();
    }


    public boolean existsIndex(Class clazz){
        IndexOperations indexOperations = elasticsearchOperations.indexOps(clazz);
        return indexOperations.exists();
    }

    public String getIndexName(Class clazz){
        IndexOperations indexOperations = elasticsearchOperations.indexOps(clazz);
        return indexOperations.getIndexCoordinates().getIndexName();
    }

    /**
     * @描述
     * @参数[elasticsearchTemplate, indexName, clazz]
     * @返回值 boolean
     * @创建人 孙中泉
     * @创建时间 2019/10/18
     * @修改人和其他信息
     */
    public <T> boolean createIndexByNameAndTemplateDocument(String indexName, Class<T> clazz) {
        IndexOperations templateIndexOperations = elasticsearchOperations.indexOps(clazz);
        Map setting = templateIndexOperations.getSettings();
        /**
         * 从class中读取的setting包括，去掉其中不需要的
         * //                "index.refresh_interval" -> "1s"
         * //                "index.creation_date" -> "1571364335513"
         * //                "index.uuid" -> "v38frBFLRlOthuv8GrA08A"
         * //                "index.version.created" -> "6080099"
         * //                "index.provided_name" -> "home_template"
         * //                "index.number_of_replicas" -> "0"
         * //                "index.store.type" -> "fs"
         * //                "index.number_of_shards" -> "1"
         */
        setting.remove("index.creation_date");
        setting.remove("index.uuid");
        setting.remove("index.version.created");
        setting.remove("index.provided_name");
        setting.remove("index.store.type");
        setting.remove("index.539.settings.index.creation_date");
        setting.put("index.max_result_window", 1000000);
        Map mapping = templateIndexOperations.getMapping();
        IndexCoordinates indexCoordinates = IndexCoordinates.of(indexName);
        IndexOperations indexOperations = elasticsearchOperations.indexOps(indexCoordinates);
        Document indexSetting = Document.create();
        indexSetting.putAll(setting);
        while (true){
            try {
                indexOperations.create(indexSetting);
                break;
            }catch (Exception e){
                e.printStackTrace();
                LOGGER.error("索引名:" + indexName + ", 索引创建失败,等待1分钟后重试");
                try {
                    Thread.sleep(60*1000);
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
        }
        while (true){
            try {
                Document indexMapping = Document.create();
                indexMapping.putAll(mapping);
                indexOperations.putMapping(indexMapping);
                break;
            }catch (Exception e){
                e.printStackTrace();
                LOGGER.error("索引名:" + indexName + ", 创建映射失败,等待1分钟后重试");
                try {
                    Thread.sleep(60*1000);
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
        }

        return  true;
    }

    public <T> boolean createIndexIfNotExists(String indexName, Class<T> documentClass) {
        IndexOperations indexOperations = elasticsearchOperations.indexOps(IndexCoordinates.of(indexName));
        if (!indexOperations.exists()) {
            synchronized (ElasticsearchOperationstUtil.class) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (!indexOperations.exists()) {
                    this.createDocument( documentClass);
                }
            }
        }
        return true;
    }

    public <T> boolean createDocument(Class<T> documentClass) {
        IndexOperations indexOperations = elasticsearchOperations.indexOps(documentClass);
        return indexOperations.create() && indexOperations.putMapping();
    }

    public <T> boolean createDocumentRemoveIfExist(String indexName, Class<T> documentClass) {
        IndexOperations indexOperations = elasticsearchOperations.indexOps(IndexCoordinates.of(indexName));
        boolean indexExists = indexOperations.exists();
        if (indexExists) {
            indexOperations.delete();
        }
        return createDocument(documentClass);
    }

    public boolean removeAllIndexAliasNameIfExists(String indexName) {
        IndexCoordinates indexCoordinates = IndexCoordinates.of(indexName);
        IndexOperations indexOperations = elasticsearchOperations.indexOps(indexCoordinates);
        if (indexOperations.exists()) {
            AliasActions aliasActions = new AliasActions();
            Map<String, Set<AliasData>> aliasesForIndexMap = indexOperations.getAliasesForIndex(indexName);
            List<String> aliasList = new LinkedList<>();
            for (Map.Entry<String, Set<AliasData>> aliasData : aliasesForIndexMap.entrySet()) {
                Set<AliasData> aliasDataSet = aliasData.getValue();
                aliasDataSet.stream().forEach( a-> aliasList.add(a.getAlias()));
            }
            AliasAction.Remove aliasAction = new AliasAction.Remove(AliasActionParameters.builder().withIndices(indexName).withAliases(aliasList.toArray(new String[]{})).build());
            aliasActions.add(aliasAction);
            indexOperations.alias(aliasActions);
        }
        return true;
    }

    public boolean bulkIndex(List<IndexQuery> queries, String indexName) {
        int sleepTime = 60000;
        if (!queries.isEmpty()) {
            boolean flag = true;
            while (flag) {
                try {
                    elasticsearchOperations.bulkIndex(queries, IndexCoordinates.of(indexName));
                    LOGGER.debug("通用ES插入方法，成功插入：" + queries.size() + "个文档数据");
                    flag = false;
                } catch (Exception e) {
                    e.printStackTrace();
                    try {
                        Thread.sleep(sleepTime);
                        sleepTime *= 2;
                    } catch (InterruptedException ex) {
                        ex.printStackTrace();
                    }
                }
            }
        }
        return true;
    }

    public boolean deleteIndexDataById(String indexName, List<String> idList) {
        IndexCoordinates indexCoordinates = IndexCoordinates.of(indexName);
        IndexOperations indexOperations = elasticsearchOperations.indexOps(indexCoordinates);
        if (indexOperations.exists()) {
            BoolQueryBuilder queryBuilder = new BoolQueryBuilder();
            List<QueryBuilder> queryBuilderList = queryBuilder.should();
            TermsQueryBuilder termsQueryBuilder = new TermsQueryBuilder("_id", idList);
            queryBuilderList.add(termsQueryBuilder);
            NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
            nativeSearchQueryBuilder.withQuery(queryBuilder);
            elasticsearchOperations.delete(nativeSearchQueryBuilder.build(), indexCoordinates);
            indexOperations.refresh();
        }
        return true;
    }

}
